import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.util.NoSuchElementException;
import java.util.Vector;

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.evaluation.NominalPrediction;
import weka.classifiers.functions.Logistic;
import weka.classifiers.functions.SMO;
import weka.classifiers.functions.SimpleLogistic;
import weka.classifiers.lazy.IB1;
import weka.classifiers.lazy.IBk;
import weka.classifiers.lazy.KStar;
import weka.classifiers.lazy.LWL;
import weka.classifiers.meta.AdaBoostM1;
import weka.classifiers.meta.Bagging;
import weka.classifiers.meta.ClassificationViaRegression;
import weka.classifiers.meta.Decorate;
import weka.classifiers.meta.LogitBoost;
import weka.classifiers.meta.RotationForest;
import weka.classifiers.rules.DTNB;
import weka.classifiers.rules.NNge;
import weka.classifiers.trees.ADTree;
import weka.classifiers.trees.DecisionStump;
import weka.classifiers.trees.FT;
import weka.classifiers.trees.LADTree;
import weka.classifiers.trees.LMT;
import weka.classifiers.trees.RandomForest;
import weka.core.FastVector;
import weka.core.Instances;

public class MachineLearning {
	Classifier test;

	DataBean statString = null;
	Evaluation evaluation = null;

	public MachineLearning() {
	}

	public DataBean calculateML(Vector<DataBean> testData, String testString) {
		if (testString == null)
			throw new NoSuchElementException();

		Instances testset = null;

		try {
			// Read all the instances in the file
			StringReader testFile = new StringReader(testString);
			testset = new Instances(testFile);
		} catch (IOException e1) {
			System.out.println(testString);
			e1.printStackTrace();
		}

		// Make the last attribute be the class
		testset.setClassIndex(testset.numAttributes() - 1);

		// System.out.println(tree.toString());

		try {

			evaluation.evaluateModel(test, testset);
						
			// Percentage of spam emails incorrectly classified as non-spam
			double falseNegativeRate = evaluation.falseNegativeRate(1);
			// Percentage of non-spam emails incorrectly classified as spam
			double falsePositiveRate = evaluation.falsePositiveRate(1);
			// Percentage of non-spam emails correctly classified as non-spam
			double trueNegativeRate = evaluation.trueNegativeRate(1);
			// Percentage of spam emails correctly classified as spam
			double truePositiveRate = evaluation.truePositiveRate(1);
			
			System.out.print( falseNegativeRate + ",");
			System.out.print( falsePositiveRate+ ",");
			System.out.print(trueNegativeRate+ ",");
			System.out.println(truePositiveRate);
	
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// System.out.println(evaluation.toSummaryString(true));
		// System.out.println(evaluation.toMatrixString());

		FastVector extracted = evaluation.predictions();

		// System.out.println(docString.size());

		// System.out.println("Predicted String Size: " + extracted.size() +
		// "\nActual String Size: " +docString.size());

		// TODO: look at element to see if it changes

		// Parse to tell whether there is more than one word in a certain
		// string.
		// System.out.println("Analyzed Size "+testData.size());
		// System.out.println("Extracted Size "+extracted.size());
		for (int i = 0; i < extracted.size(); i++) {
			DataBean statString = testData.get(i);
			if ((((NominalPrediction) extracted.elementAt(i)).predicted()) == 0) {
				statString.setPredicted("0");
			} else
				statString.setPredicted("1");
		}

		return statString;
	}

	/**
	 *
	 * This sets up Machine Learning
	 * 
	 *
	 * @param training training sring
	 * @param alg what algorithm to run
	 * @param numOfIterations Number of iterations for algorithm
	 * @return 
	 */

	public String mlSetup(String training, int alg, int numOfIterations) {
		Instances trainFile = null;

		StringReader trainingSetReader;

		try {
			trainingSetReader = new StringReader(training);
			trainFile = new Instances(trainingSetReader);
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		trainFile.setClassIndex(trainFile.numAttributes() - 1);
		String algorithmName;
		
		switch (alg) {
		case 1:
			test = new ClassificationViaRegression();
			algorithmName = "Regression";
			break;
		case 2:
			test = new KStar();
			algorithmName = "KStar";
			break;
		case 3:
			test = new LWL();
			algorithmName = "LWL";
			break;
		case 4:
			test = new LogitBoost();
			algorithmName = "Logit Boost";
			break;
		case 5:
			test = new ADTree();
//			((ADTree) test).setNumOfBoostingIterations(numOfIterations);
			algorithmName = "AD Tree";
			break;
		case 6:
			test = new DTNB();
			algorithmName = "DTNB";
			break;
		case 7:
			test = new DecisionStump();
			algorithmName = "Decision Stump";
			break;
		case 8:
			test = new Bagging();
			algorithmName = "Bagging";
			break;
		case 9:
			test = new IB1();
			algorithmName = "IB1";
			break;
		case 10:
			test = new IBk();
			((IBk)test).setKNN(3);
			algorithmName = "IBk";
			break;
		case 11:
			test = new SimpleLogistic();
			algorithmName = "Simple Logistic Regression";
			break;
		case 12:
			test = new LMT();
			algorithmName = "LMT";
			break;
		case 13:
			test = new NNge();
			algorithmName = "NNge";
			break;
		case 14:
			test = new FT();
			algorithmName = "FT";
			break;
		case 15:
			test = new SMO();
			algorithmName = "SMO";
			break;
		case 16:
			test = new LADTree();
//			((LADTree) test).setNumOfBoostingIterations(50);
			algorithmName = "LAD Tree";
			break;
		case 17:
			test = new RandomForest();
//			((RandomForest) test).setNumTrees(numOfIterations);
			algorithmName = "Random Forest";
			break;
		case 18:
			test = new NaiveBayes();
			algorithmName = "Naive Bayes";
			break;
		case 19:
			test = new Decorate();
			algorithmName = "Decorate";
			break;
		case 20:
			test = new AdaBoostM1();
			algorithmName = "Ada Boost M1";
			break;
		case 21:
			test = new Logistic();
			algorithmName = "Logistic";
			break;
		default:
			test = new RotationForest();
//			((RotationForest) test).setNumIterations(numOfIterations);
			algorithmName = "Rotation Forest";
		}

		try {
			test.buildClassifier(trainFile);
			evaluation = new Evaluation(trainFile);
			return algorithmName;

		} catch (Exception e) {
			System.out.println("Error occured while making test.");
			System.out.println(e);
			System.exit(0);
		}
		
		return null;
	}

	public Evaluation getEvaluation() {
		return evaluation;
	}
	public Classifier getTest() {
		return test;
	}
}
